package com.twang.stock.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.twang.stock.dto.MinutesFixDto;
import com.twang.stock.entity.*;
import com.twang.stock.mapper.CompanyCodeInfoMapper;
import com.twang.stock.mapper.CompanyStockMinutesMapper;
import com.twang.stock.redis.RedisUtil;
import com.twang.stock.service.CompanyStockMinutesService;
import com.twang.stock.service.jinyan.CommonService;
import com.twang.stock.threadPool.AsycTaskExecutorConfig;
import com.twang.stock.utils.date.DateUtils;
import com.twang.stock.utils.date.HolidayCopy;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.assertj.core.util.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
public class CompanyStockMinutesServiceImpl implements CompanyStockMinutesService {

    @Resource
    private CompanyStockMinutesMapper companyStockMinutesMapper;

    @Autowired
     private CommonService commonService;

    @Autowired
    private RedisUtil redisUtil;

    private static String STOCK_VOL_KEY_REX="max_vol_";

    private static String STOCK_30_VOL_KEY_REX="day_30_min_vol_";


    private static String STOCK_VOL_="total_vol_";

    private static String stock_30_min_vol_="STOCK_30_min_VOL_";

    private static String COMPANY_STOCK_CODE_LIST = "COMPANY_STOCK_CODE_LIST";

    @Resource
    private CompanyCodeInfoMapper companyCodeInfoMapper;

    @Autowired
    private AsycTaskExecutorConfig asycTaskExecutorConfig;


    @Override
    public List<BigDecimal> getMaxTradeMinutesVol(String code,List<String> days ) {
        String stockKey = STOCK_VOL_ + "_" + HolidayCopy.getRealToday() + "_" + code;
        String stockKeyDay = STOCK_VOL_ + "day_" + HolidayCopy.getRealToday() + "_" + code;
        Object object = redisUtil.get(stockKey);
        if (null != object && null != redisUtil.get(stockKeyDay)) {
            days.addAll((List<String>) redisUtil.get(stockKeyDay));
            return (List<BigDecimal>) object;
        }
        String value = commonService.getValue(CommonService.UtilsKey.date_无效日期);
        List<String> unDates = Lists.newArrayList(value.split(","));
        List<String> dates = companyStockMinutesMapper.queryDates();
        for (String date : dates) {
            if (unDates.contains(date)) {
                continue;
            }
            BigDecimal val = getMaxVol(date, code);
            redisUtil.set(STOCK_VOL_KEY_REX + date + code, val);
        }
        //求平均值
        List<BigDecimal> vols = Lists.newArrayList();
        List<String> daysTemp = Lists.newArrayList();
        dates.stream().forEach(day -> {
            Object obj = redisUtil.get(STOCK_VOL_KEY_REX + day + code);
            if (null != obj) {
                BigDecimal dayVol = new BigDecimal(obj.toString());
                vols.add(dayVol);
                daysTemp.add(day);
            }
        });
        days.addAll(daysTemp);
        BigDecimal avgVol = (vols.stream().map(detail -> Optional.ofNullable(detail).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add)).divide(new BigDecimal(vols.size()), 2, BigDecimal.ROUND_HALF_UP);
        redisUtil.set(stockKey, vols);
        redisUtil.set(stockKeyDay, days);
        return vols;
    }

    @Override
    public List<BigDecimal> getTrade30MinutesVol(String code,List<String> days ) {
        String volkey=IndexServiceImpl.STOCK_TRADE_SUM_30_vol +"__"+ HolidayCopy.getRealToday()+"__"+code;
        String dayKey=IndexServiceImpl.STOCK_TRADE_SUM_30_day +"__"+ HolidayCopy.getRealToday()+"__"+code;
        Object object = redisUtil.get(volkey);
        Boolean isCache= DateUtils.JudgeTime("09:30:00", "10:10:00");
        if (null != object && !isCache) {
            days.addAll((List<String>) redisUtil.get(dayKey));
            return (List<BigDecimal>) object;
        }
        //没有缓存
        List<String> dates = companyStockMinutesMapper.queryDates();
        for (String date : dates) {
            String newCode = code;
            if (code.startsWith("3") || code.startsWith("0")) {
                newCode = "sz" + code;
            } else {
                newCode = "sh" + code;
            }
            BigDecimal val = getVolByMinutes(date, newCode);
            redisUtil.set(STOCK_30_VOL_KEY_REX + date + code, val, 60 * 60 * 24);
        }
        //求平均值
        List<BigDecimal> vols = Lists.newArrayList();
        List<String> daysTemp = Lists.newArrayList();
        dates.stream().forEach(day -> {
            Object obj = redisUtil.get(STOCK_30_VOL_KEY_REX + day + code);
            if (null != obj) {
                BigDecimal dayVol = new BigDecimal(obj.toString());
                vols.add(dayVol);
                daysTemp.add(day);
            }
        });
        days.addAll(daysTemp);
        redisUtil.set(dayKey,days,60*60*24);
        redisUtil.set(volkey,vols,60*60*24);
       // BigDecimal avgVol = (vols.stream().map(detail -> Optional.ofNullable(detail).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add)).divide(new BigDecimal(vols.size()), 2, BigDecimal.ROUND_HALF_UP);
        return vols;
    }

    /**
     * 获取最大的Vol
     * @param date
     * @param code
     * @return
     */
    @Override
    public BigDecimal getMaxVol(String date, String code){
        Example example = new Example(CompanyStockMinutes.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("companyCode", code);
        criteria.andEqualTo("date", date);
        criteria.andNotIn("minutes", Lists.newArrayList(commonService.getValue(CommonService.UtilsKey.minutes_无效时分).split(",")));
        example.orderBy("id").asc();
        List<CompanyStockMinutes> companyStockMinutes = companyStockMinutesMapper.selectByExample(example);
        BigDecimal val = companyStockMinutes.stream().filter(stock -> stock.getVol() != null).map(CompanyStockMinutes::getVol).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        return val;
    }


    /**
     * 获取最大的Vol
     * @param date
     * @param code
     * @return
     */
    private BigDecimal getVolByMinutes(String date, String code){
        List<CompanyStockMinutes> companyStockMinutes = companyStockMinutesMapper.queryCompanyStockMinutes(code,"1000",date);
        if(!CollectionUtils.isEmpty(companyStockMinutes)){
            BigDecimal val = companyStockMinutes.get(0).getTradeVol();
            return val;
        }else{
            //
            companyStockMinutes = companyStockMinutesMapper.queryCompanyStockMinutes(code,"0959",date);
            if(!CollectionUtils.isEmpty(companyStockMinutes)){
                BigDecimal val = companyStockMinutes.get(0).getTradeVol();
                return val;
            }else{
                companyStockMinutes =companyStockMinutesMapper.queryCompanyStockMinutes(code,"1001",date);
                if(!CollectionUtils.isEmpty(companyStockMinutes)){
                    BigDecimal val = companyStockMinutes.get(0).getTradeVol();
                    return val;
                }
            }
            return BigDecimal.ZERO;
        }
    }

    /**
     * 修复数据
     */
    @Override
    public void fixTodayMinutes() {
        List<CompanyCodeInfo> list;
        Object object = redisUtil.get(COMPANY_STOCK_CODE_LIST);
        if (null != object) {
            list = (List<CompanyCodeInfo>) object;
        } else {
            list = companyCodeInfoMapper.selectAll();
            redisUtil.set(COMPANY_STOCK_CODE_LIST,list);
        }
        list.forEach(item -> {
            asycTaskExecutorConfig.taskQueueExecutor().execute(() -> {
                // HttpClient 超时配置
                RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD)
                        .setConnectionRequestTimeout(6000).setConnectTimeout(6000).build();
                CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build();
                String url = "http://quotes.money.163.com/service/zhubi_ajax.html?symbol=" + item.getCompanyCode().replace("sh", "").replace("sz", "") + "&end=13:30:00";
                getData(url, item.getCompanyCode(), httpClient, "1129", "1330");
                url = "http://quotes.money.163.com/service/zhubi_ajax.html?symbol=" + item.getCompanyCode().replace("sh", "").replace("sz", "") + "&end=14:20:00";
                getData(url, item.getCompanyCode(), httpClient, "1329", "1420");
                url = "http://quotes.money.163.com/service/zhubi_ajax.html?symbol=" + item.getCompanyCode().replace("sh", "").replace("sz", "") + "&end=15:00:00";
                getData(url, item.getCompanyCode(), httpClient, "1419", "1500");
            });
        });
    }

    @Override
    public String getYesterday() {
        List<String> datas= companyStockMinutesMapper.queryDates();
        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(datas)){
            return datas.get(datas.size()-2);
        }
        return null;
    }

    private void getData(String url,String code,CloseableHttpClient httpClient,String startTime,String endTime) {
        try {
            String result = getHtmlStrByUrl(url, httpClient);
            String listStr = JsonPath.read(result, "$.zhubi_list").toString();
            List<MinutesFixDto> lists = JSONObject.parseArray(listStr, MinutesFixDto.class);
            lists.stream().forEach(t -> {
                t.setDateStr(t.getDateStr().replace(":", "").substring(0, 4));
            });
            Map<String, List<MinutesFixDto>> listsMap = lists.stream().collect(Collectors.groupingBy(t -> t.getDateStr()));
            Example example = new Example(CompanyStockMinutes.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("companyCode", code);
            criteria.andEqualTo("date", "20220321");
            criteria.andGreaterThan("minutes",startTime);
            criteria.andLessThan("minutes",endTime);
            example.orderBy("id").asc();
            List<CompanyStockMinutes> companyStockMinutes = companyStockMinutesMapper.selectByExample(example);
            if (!CollectionUtils.isEmpty(companyStockMinutes)) {
                for (int i = 1; i < companyStockMinutes.size(); i++) {
                    CompanyStockMinutes s = companyStockMinutes.get(i);
                    List<MinutesFixDto> minutesFixDtos = listsMap.get(s.getMinutes());
                    if (!CollectionUtils.isEmpty(minutesFixDtos)) {
                        BigDecimal vol = minutesFixDtos.stream().map(detail -> Optional.ofNullable(detail.getVolumeInc()).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(100), 0, BigDecimal.ROUND_HALF_UP);
                        BigDecimal amount = minutesFixDtos.stream().map(detail -> Optional.ofNullable(detail.getTurnoverInc()).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(100), 0, BigDecimal.ROUND_HALF_UP);
                        CompanyStockMinutes param = new CompanyStockMinutes();
                        param.setCompanyCode(s.getCompanyCode());
                        param.setDate("20220322");
                        param.setMinutes(companyStockMinutes.get(i - 1).getMinutes());
                        CompanyStockMinutes temp = companyStockMinutesMapper.selectOne(param);
                        if (null == temp) {
                            param.setMinutes(companyStockMinutes.get(i - 2).getMinutes());
                            temp = companyStockMinutesMapper.selectOne(param);
                        }
                        CompanyStockMinutes data = new CompanyStockMinutes();
                        BeanUtils.copyProperties(s, data);
                        data.setDate("20220322");
                        data.setMinutes(s.getMinutes());
                        data.setId(null);
                        data.setPrice(minutesFixDtos.get(0).getPrice());
                        if (null != temp) {
                            data.setTradeVol(temp.getTradeVol().add(vol));
                            data.setAmount(temp.getTradeSum().add(amount));
                        }
                        data.setAmount(amount);
                        data.setVol(vol);
                        data.setCreateTime(new Date());
                        //
                        param.setMinutes(s.getMinutes());
                        List<CompanyStockMinutes> temps = companyStockMinutesMapper.select(param);
                        if (!CollectionUtils.isEmpty(temps)) {
                            int num = companyStockMinutesMapper.delete(param);
                        }
                        companyStockMinutesMapper.insert(data);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getHtmlStrByUrl(String url, CloseableHttpClient httpClient) throws Exception {
        HttpGet httpGetForFName = new HttpGet(url);
        httpGetForFName.addHeader("Content-Type", "text/html; charset=gbk");
        httpGetForFName.addHeader("Accept-Encoding", "gzip, deflate");
        httpGetForFName.addHeader("Connection", "keep-alive");
        // 发送请求，并执行
        CloseableHttpResponse response;
        String html = "";
        try {
            response = httpClient.execute(httpGetForFName);
            // response植入cookie
            Header[] ssoResponseHeader = response.getHeaders("Set-Cookie");
            InputStream in = response.getEntity().getContent();
            html = convertStreamToString(in);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return html;
    }

    public static String convertStreamToString(InputStream in) {
        StringBuilder sb = new StringBuilder();
        try {

            InputStreamReader isr = new InputStreamReader(in, "utf-8");
            BufferedReader reader = new BufferedReader(isr);
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "/n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();

    }
}
